package net.pinnoocle.playservice.service;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import net.pinnoocle.playcommon.constant.Constant;
import net.pinnoocle.playcommon.exception.ShopException;
import net.pinnoocle.playcommon.response.ErrorCode;
import net.pinnoocle.playcommon.utils.BigDecimalMathUtil;
import net.pinnoocle.playcommon.utils.wxPay.MyConfig;
import net.pinnoocle.playcommon.utils.wxPay.WXPay;
import net.pinnoocle.playcommon.utils.wxPay.WXPayConstants;
import net.pinnoocle.playcommon.utils.wxPay.WXPayUtil;
import net.pinnoocle.playdao.entity.*;
import net.pinnoocle.playservice.service.impl.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.InetAddress;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author ye
 * @Description
 * @create 2021-10-29 19:57
 */
@Service("payService")
@Slf4j
public class PayService {

    @Value("${upload.certPath}")
    private String baseCertPath;

    @Autowired
    ShopWechatSettingServiceImpl wechatSettingService;
    @Autowired
    ShopServiceImpl shopService;
    @Autowired
    UserAppointOrderServiceImpl userAppointOrderService;
    @Autowired
    OrderServiceImpl orderService;
    @Autowired
    GroupServiceImpl groupService;
    @Autowired
    UserServiceImpl userService;
    @Autowired
    ShopFinanceServiceImpl shopFinanceService;

    /**
     * 返回成功xml
     */
    private String resSuccessXml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";

    /**
     * 返回失败xml
     */
    private String resFailXml = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml>";

    /**
     * 统一下单
     * @param shopId
     * @param openId
     * @param totalFree
     * @param orderType 1 定金订单 2 支付订单
     * @return
     * @throws Exception
     */
    public PayInfo payOrder(String shopId, String openId, Double totalFree,Integer orderType) throws Exception {
        ShopWechatSetting setting = wechatSettingService.getOne(new QueryWrapper<ShopWechatSetting>()
                .lambda()
                .eq(ShopWechatSetting::getShopId, shopId));
        if(Objects.isNull(setting)){
            throw new ShopException(ErrorCode.PARAMS_GET_ERROR,"小程序支付信息未配置");
        }
        if(StringUtils.isBlank(setting.getAppId()) ||
                StringUtils.isBlank(setting.getMchId()) ||
                StringUtils.isBlank(setting.getSecretKey()) ||
                StringUtils.isBlank(setting.getCertPath())){
            throw new ShopException(ErrorCode.PARAMS_GET_ERROR,"小程序支付信息未配置");
        }
        //证书路径
        String certPath = setting.getCertPath();
        log.info("certPath==="+certPath);
        String[] path = certPath.split("_");
        String certRealPath = baseCertPath + path[0] +"/"+ certPath;
        log.info("certRealPath==="+certRealPath);
        File dest = new File(certRealPath);
        if(!dest.exists()){
            throw new ShopException(ErrorCode.PARAMS_GET_ERROR,"小程序支付证书未配置!");
        }
        PayInfo payInfo = new PayInfo();
        HashMap<String, String> resultMap = new HashMap<>();
        MyConfig config = new MyConfig(setting.getAppId(),setting.getMchId(),setting.getSecretKey(),certRealPath);
        WXPay wxpay = new WXPay(config);
        //生成的随机字符串
        String nonce_str = WXPayUtil.generateNonceStr();
        //获取客户端的ip地址
        InetAddress ipAddress = InetAddress.getLocalHost();
        String spbill_create_ip = ipAddress.getHostAddress();
        //支付金额，需要转成字符串类型，否则后面的签名会失败
        int total_fee = (int) BigDecimalMathUtil.mul(totalFree,100);
        //商户订单号
        String out_trade_no = WXPayUtil.generateNonceStr();
        //统一下单接口参数
        HashMap<String, String> data = new HashMap<String, String>();
        data.put("appid",setting.getAppId());
        data.put("mch_id",setting.getMchId());
        data.put("nonce_str", nonce_str);
        data.put("body", "剧本杀定金支付");
        data.put("out_trade_no",out_trade_no);
        data.put("total_fee", String.valueOf(total_fee));
        data.put("spbill_create_ip", spbill_create_ip);
        data.put("notify_url", (orderType == 1) ? Constant.APPOINT_NOTIFY_URL : Constant.ORDER_NOTIFY_URL);
        data.put("trade_type","JSAPI");
        data.put("openid", openId);
        try {
            Map<String, String> rMap = wxpay.unifiedOrder(data);
            System.out.println("统一下单接口返回: " + rMap);
            String return_code = rMap.get("return_code");
            String result_code = rMap.get("result_code");
            //String nonceStr = nonce_str;//WXPayUtil.generateNonceStr();
            payInfo.setNonceStr(nonce_str);
            resultMap.put("nonceStr", nonce_str);
            Long timeStamp = System.currentTimeMillis() / 1000;
            if ("SUCCESS".equals(return_code) && return_code.equals(result_code)) {
                String prepayid = rMap.get("prepay_id");
                payInfo.setPackageX("prepay_id="+prepayid);
                resultMap.put("package", "prepay_id="+prepayid);
                resultMap.put("signType", "HMACSHA256");
                payInfo.setSignType("HMACSHA256");
                resultMap.put("timeStamp", timeStamp + "");
                payInfo.setTimeStamp(timeStamp+"");
                resultMap.put("appId",setting.getAppId());
                payInfo.setAppId(setting.getAppId());
                String sign = WXPayUtil.generateSignature(resultMap, setting.getSecretKey(), WXPayConstants.SignType.HMACSHA256);
                resultMap.put("paySign", sign);
                payInfo.setPaySign(sign);
                payInfo.setOutTradeNo(out_trade_no);
                return payInfo;
            }else{
                throw new ShopException(ErrorCode.PARAMS_GET_ERROR,rMap.get("return_msg"));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return  null;
        }
    }

    /**
     * 订单查询
     * @param outTradeNo
     * @return
     * @throws Exception
     */
    public OrderInfoVo orderQuery(String outTradeNo) throws Exception {
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        String shopId = userService.getGlobalShopId();
        ShopWechatSetting setting = wechatSettingService.getOne(new QueryWrapper<ShopWechatSetting>()
                .lambda()
                .eq(ShopWechatSetting::getShopId, shopId));
        if(Objects.isNull(setting)){
            throw new ShopException(ErrorCode.PARAMS_GET_ERROR,"小程序支付信息未配置");
        }
        if(StringUtils.isBlank(setting.getAppId()) ||
                StringUtils.isBlank(setting.getMchId()) ||
                StringUtils.isBlank(setting.getSecretKey()) ||
                StringUtils.isBlank(setting.getCertPath())){
            throw new ShopException(ErrorCode.PARAMS_GET_ERROR,"小程序支付信息未配置");
        }
        String certPath = setting.getCertPath();
        String[] path = certPath.split("_");
        String certRealPath = baseCertPath + path[0] +"/"+ certPath;
        File dest = new File(certRealPath);
        if(!dest.exists()){
            throw new ShopException(ErrorCode.PARAMS_GET_ERROR,"小程序支付证书未配置");
        }
        HashMap<String, String> data = new HashMap<>();
        data.put("out_trade_no",outTradeNo);
        MyConfig config = new MyConfig(setting.getAppId(),setting.getMchId(),setting.getSecretKey(),certRealPath);
        WXPay wxpay = new WXPay(config);
        Map<String, String> resultMap = wxpay.orderQuery(data);
        String tradeState = resultMap.getOrDefault("trade_state","");
        if(Objects.equals(tradeState, "SUCCESS")){
            String transactionId = resultMap.getOrDefault("transaction_id", "");
            String payTime = resultMap.getOrDefault("time_end","");
            DateTime payDate = DateUtil.parse(payTime);
            orderInfoVo.setTradeState(tradeState);
            orderInfoVo.setTransactionId(transactionId);
            orderInfoVo.setPayTime(payDate);
        }
        return orderInfoVo;
    }

    /**
     * 订单异步通知
     * @param request
     * @param response
     * @param orderType 1 支付订单的定金 2 支付玩剧本的订单
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String payNotify(HttpServletRequest request, HttpServletResponse response ,Integer orderType) {
        String resXml = "";
        InputStream inStream;
        try {
            inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            WXPayUtil.getLogger().info("payNotify:微信支付----start----");
            // 获取微信调用我们notify_url的返回信息
            String result = outSteam.toString("utf-8");
            WXPayUtil.getLogger().info("payNotify:微信支付----result----=" + result);
            // 关闭流
            outSteam.close();
            inStream.close();
            // xml转换为map
            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
            if (WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get(WXPayConstants.RESULT_CODE))) {
                String tradeState = resultMap.getOrDefault("trade_state","");
                String outTradeOut = resultMap.getOrDefault("out_trade_no","");
                String transactionId = resultMap.getOrDefault("transaction_id", "");
                String payTime = resultMap.getOrDefault("time_end","");
                DateTime payDate = DateUtil.parse(payTime);
                if(WXPayConstants.SUCCESS.equalsIgnoreCase(tradeState)){
                    //定金订单
                    if(orderType == 1){
                        UserAppointOrder appointOrder = userAppointOrderService.getOne(new QueryWrapper<UserAppointOrder>()
                                .lambda()
                                .eq(UserAppointOrder::getOutTradeNo, outTradeOut)
                                .last("LIMIT 1"));
                        if(Objects.nonNull(appointOrder) && appointOrder.getHasPay() == 0){
                            appointOrder.setHasPay(1);
                            appointOrder.setTransactionId(transactionId);
                            appointOrder.setPayTime(payDate);
                            boolean handleResult = userAppointOrderService.updateById(appointOrder);
                            ShopFinance shopFinance = new ShopFinance();
                            shopFinance.setShopId(appointOrder.getShopId());
                            shopFinance.setAmount(appointOrder.getPayAmount());
                            shopFinance.setFinanceType(0);
                            shopFinance.setSourceName("支付定金");
                            shopFinance.setOrderNo(appointOrder.getOrderNo());
                            shopFinance.setCreatedTime(new Date());
                            shopFinanceService.save(shopFinance);
                            if(handleResult){
                                boolean joinResult = groupService.joinGroupDo(appointOrder.getGroupId(), appointOrder.getUserId());
                                if(!joinResult){
                                    WXPayUtil.getLogger().info("payNotify:微信支付回调：加入组局失败===>" + resultMap.get("out_trade_no"));
                                }
                                WXPayUtil.getLogger().info("payNotify:微信支付回调：修改定金订单成功===>" + resultMap.get("out_trade_no"));
                            }
                        }
                    }else{
                        Order userOrder = orderService.getOne(new QueryWrapper<Order>()
                                .lambda()
                                .eq(Order::getOutTradeNo, outTradeOut)
                                .last("LIMIT 1"));
                        if(Objects.nonNull(userOrder) && userOrder.getHasPay() == 0){
                            userOrder.setHasPay(1);
                            userOrder.setTransactionId(transactionId);
                            userOrder.setPayTime(payDate);
                            boolean handleResult = orderService.updateById(userOrder);
                            orderService.handleOrder(userOrder);
                            if(handleResult){
                                WXPayUtil.getLogger().info("payNotify:微信支付回调：修改定金订单成功===>" + resultMap.get("out_trade_no"));
                            }
                        }
                    }
                }
            } else {
                WXPayUtil.getLogger().error("payNotify:支付失败,错误信息：" + resultMap.get(WXPayConstants.ERR_CODE_DES));
                resXml = resFailXml;
            }
        } catch (Exception e) {
            WXPayUtil.getLogger().error("payNotify:支付回调发布异常：", e);
        } finally {
            try {
                // 处理业务完毕
                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                out.write(resXml.getBytes());
                out.flush();
                out.close();
            } catch (IOException e) {
                WXPayUtil.getLogger().error("payNotify:支付回调发布异常:out：", e);
            }
        }
        return "success";
    }
}
